#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctype.h>
#include <cassert>

#include "mapa.h"
#include "algoritmos.h"


#define MAX_LINEA 80
#define entrada cin


using namespace std;

void mostrarAyuda() {
	cout << "Uso:  ej2  ALGORITMO" << endl << endl; 
	cout << "Algoritmos:" << endl;
	cout << "0        Algoritmo de Dijkstra" << endl;
	cout << "1        Algoritmo A* - Heurística con distancia euclideana" << endl;
	cout << "2        Algoritmo A* - Heurística con distancia manhattan" << endl;
	cout << "3        Algoritmo A* - Otra heuristica " << endl;
	cout << "4        Algoritmo A* - Otra heuristica más" << endl;
}



int parsearRestricciones(char *linea, lectura_casilla* p, unsigned int& restric) {
// ! solo funciona si x,y,t,restric estan en una misma linea (i.e. no hay CR LF)
	restric = 0;
	
	int i=0;
	
	//Parsea x de la casilla
	p->x = atol(linea);

	while (isdigit(linea[i])) i++;
	while (isspace(linea[i])) i++;

	//Parsea y de la casilla
	p->y = atol(linea+i);

	while (isdigit(linea[i])) i++; 
	while (isspace(linea[i])) i++;
	if(linea[i] == '\0')
		return -1;
	
	//Parsea tiempo
	p->tiempo = atol(linea+i);
	
	
	while (!ispunct(linea[i]) && !(linea[i]=='v')) i++; 
	
	//Parsea restricciones
	while (linea[i] !='\0') {
		switch(linea[i]) {
			// Indica en qué direcciones puede moverse
			case '.': restric &= FULL_RESTRIC; return 1; // unica restriccion y fin de ciclo
			case '^': restric |= ARRIBA; break;
			case 'v': restric |= ABAJO; break;
			case '<': restric |= IZQUIERDA; break;
			case '>': restric |= DERECHA; break;
			case '+': restric |= SIN_RESTRIC;	return 1; // unica restriccion y fin de ciclo
			default: return -1;	
		}
		i++;
	}
	
	return 1;
	
}


int main (int argc, char** argv)
{
	
/*************  VARIABLES  *****************/
	int opcion;
			
	unsigned int X, Y;
	unsigned int xi, yi;
	unsigned int xf, yf;
	
	lectura_casilla param_casilla;
	
	unsigned int restricciones; //en qué dir. sí puede moverse
	
	//Buffer para parsear las lineas con restricciones
	char linea[MAX_LINEA + 1];
	
	bool finCasos = false;
	bool siguienteCaso;
	
	Mapa* M;
	
	

/*************  PARAMETROS DEL PROGRAMA  *****************/	

	if(argc != 2) {
		mostrarAyuda();
		return -1;
	}
	else {
		opcion = atoi(argv[1]);
		if (opcion < DIJKSTRA || opcion > ASTAR_HEUR4) {
			mostrarAyuda();
			return -1;
		}
	}
	


/************* ACA EMPIEZA  EL PROGRAMA  ******************/

	while (!finCasos)
	{		
		
		siguienteCaso = false;
		
		// Lee las dimensiones del mapa, si aún hay para leer
		entrada >> X;
		if (entrada.fail()) finCasos = true;
					
		else {
			entrada>> Y;	
			assert( !(X > 0 && Y == 0) && !(X == 0 && Y > 0));
		
			if ( X == 0  &&  Y == 0 )
				finCasos = true;
		
			else {

				M = new Mapa(X,Y);
				casillero inicio, destino;
			
				entrada >> xi >> yi;
				assert( (xi < X) && (yi < Y) );
				inicio.x = xi;
				inicio.y = yi;
			
				entrada >> xf >> yf;
				assert( (xf < X) && (yf < Y) );			
				destino.x = xf;
				destino.y = yf;				

				//~ cout << "Xi = " << X << endl;
				//~ cout << "Yi = " << Y << endl;
				//~ cout << "Xi = " << xi << endl;
				//~ cout << "Yi = " << yi << endl;
				//~ cout << "Xf = " << xf << endl;
				//~ cout << "Yf = " << yf << endl;						
				//~ cout << endl;										
			
				// Lee restricciones hasta que encuentra una linea sin ellas (sin tiempo ni direccion)
				// se asumira, que corresponde a las dimensiones del siguiente caso, y los usará en la siguiente
				// iteracion
				
				entrada.get(); //lee un '\0' que quedó (?)
				
				while(!siguienteCaso) {
				
					entrada.getline(linea, MAX_LINEA+1);
						
					if ( parsearRestricciones(linea, &param_casilla, restricciones) == -1 ) {

						siguienteCaso = true;
				
					} else {
			
						assert( (param_casilla.x < X) && (param_casilla.y < Y) );
						//~ cout << "X = " << param_casilla.x << endl;
						//~ cout << "Y = " << param_casilla.y << endl;
						//~ cout << "tiempo = " << param_casilla.tiempo << endl;
						//~ cout << "restric = " << restricciones << endl;
						//~ cout << endl;
					
						// Agregar restricciones al grafo
						(*M).establecerRestriccion(&param_casilla, restricciones);
					
					}								
				
				}
			
				// Resolver usando algun algoritmo 
				resolver(M, opcion, inicio, destino);
			
				delete M;
			
			}
		}
		
	}
	
	
	return 0;
}
